package ru.bragnikita.utils.logexplorer.logic;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.bragnikita.utils.logexplorer.logic.mock.FileSystemModificationException;
import ru.bragnikita.utils.logexplorer.model.FileWithContent;
import ru.bragnikita.utils.logexplorer.model.FilesSet;
import ru.bragnikita.utils.logexplorer.model.FilterRequest;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

/**
 * @author Nikita.Brazhnikov
 */
@Named("buffered_fs")
@ApplicationScoped
public class SimpleFileSystem implements FileSystemInterface {
	private static final Logger logger = LoggerFactory.getLogger(SimpleFileSystem.class);

	@Override
	public FilesSet getFilesSet(FilterRequest request) throws FileDownloadException {
		Preconditions.checkNotNull(request);
		final String targetDirPath = request.getDirectory();
		logger.debug("Request for file set of {}", targetDirPath);
		if (targetDirPath == null)
			throw new FileDownloadException("Requested directory is null");
		final File targetDir = new File(targetDirPath);
		if (!targetDir.exists())
			throw new FileDownloadException(targetDir, "not found");
		if (!targetDir.isDirectory())
			throw new FileDownloadException(targetDir, "not a directory");
		File[] nonRecursiveList = targetDir.listFiles();
		logger.debug("{} files in view", nonRecursiveList.length);
		return new FilesSet(Arrays.asList(nonRecursiveList));
	}

	@Override
	public FileWithContent getFileContent(String absolutePath) throws FileDownloadException {
		return new SimpleFileWithContent(new File(absolutePath));
	}

	@Override
	public void uploadFile(String directory, String filename, InputStream content) throws FileSystemModificationException {
		createOrRewriteFile(filename, directory, content);
	}

	@Override
	public int deleteFiles(Iterable<String> pathToDelete) throws FileSystemModificationException {
		logger.debug("Deleting path {}", Joiner.on(", ").skipNulls().join(pathToDelete));
		int totalCounter = 0;
		int successCounter = 0;
		for (String filepath : pathToDelete) {
			totalCounter++;
			File fileToDelete = new File(filepath);
			if (!fileToDelete.exists())
				logger.warn("File {} is not found", fileToDelete);
			else if (!fileToDelete.canWrite())
				logger.warn("File {} can not be modified", fileToDelete);
			else {
				try {
					FileUtils.forceDelete(fileToDelete);
					successCounter++;
				} catch (IOException e) {
					logger.error("Can not delete file " + fileToDelete, e);
				}
			}
		}
		logger.info("{} elements deleted, {} total", successCounter, totalCounter);
		return successCounter;
	}

	@Override
	public void saveFileContent(String filePath, InputStream content) throws FileSystemModificationException {
		final File file = new File(filePath);
		final String filename = file.getName();
		final String directory = file.getParent();
		createOrRewriteFile(filename, directory, content);
	}

	private void createOrRewriteFile(String filename, String directory, InputStream content) throws FileSystemModificationException{
		logger.debug("Saving file {} into {}", filename, directory);
		final File targetDir = new File(directory);
		if (!targetDir.exists())
			throw new FileSystemModificationException(targetDir, "Not found");
		if (!targetDir.isDirectory())
			throw new FileSystemModificationException(targetDir, "Is not a directory");
		final File uploadedFile = new File(targetDir, filename);
		if (!uploadedFile.canWrite())
			throw new FileSystemModificationException(uploadedFile, "Have no access for writing this file");
		if (uploadedFile.exists())
			logger.info("File {} is already existed and will be overridden", uploadedFile);
		try {
			FileUtils.copyInputStreamToFile(content, uploadedFile);
			logger.info("Successfully saved: {}", uploadedFile);
		} catch (IOException e) {
			throw new FileSystemModificationException(uploadedFile, "Can not save", e);
		}
	}

	private class SimpleFileWithContent implements FileWithContent {


		private File file;

		private SimpleFileWithContent(File file) {
			this.file = file;
		}

		@Override
		public String getFileName() {
			return file.getName();
		}

		@Override
		public String getAbsolutePath() {
			return file.getAbsolutePath();
		}

		@Override
		public InputStream getContentStream() throws FileDownloadException {
			try {
				return FileUtils.openInputStream(file);
			} catch (Exception e) {
				throw new FileDownloadException(file, e);
			}
		}

		@Override
		public long getLength() {
			return file.length();
		}
	}
}
